import logging
import pickle

from flask import jsonify, request
from werkzeug.exceptions import BadRequest

from app import db
from app.model.models import User, Photo, FaceModel
from app.recognition import FaceNetTorch
from app.utils import file_utils, encryption_utils
from . import bp

LOG = logging.getLogger(__name__)

facenet = FaceNetTorch.instance()


@bp.route('/users', methods=['GET'])
def get_users():
    page = request.args.get('page', 1, type=int)
    per_page = min(request.args.get('per_page', 10, type=int), 100)
    username = request.args.get('username', None)
    if username:
        query = User.query.filter(User.username.like(f'{username}%'))
    else:
        query = User.query
    data = User.to_collection_dict(query, page, per_page, 'api.get_users')
    # Add models
    user_query_list = data['items']
    user_ids = [user['id'] for user in user_query_list]
    photo_model_names = Photo.query.with_entities(Photo.user_id, Photo.model_name) \
        .filter(Photo.user_id.in_(user_ids)).group_by(Photo.user_id, Photo.model_name).all()
    photo_model_name_dict = {}
    for photo_model_name in photo_model_names:
        if photo_model_name.user_id not in photo_model_name_dict:
            photo_model_name_dict[photo_model_name.user_id] = [photo_model_name.model_name]
        else:
            photo_model_name_dict[photo_model_name.user_id].append(photo_model_name.model_name)
    for item in user_query_list:
        item['models'] = photo_model_name_dict[item['id']]
    return jsonify(success=True, data=data, status_code=200)


@bp.route('/user', methods=['POST'])
def add_user():
    data = request.json or {}
    if 'username' not in data \
            or 'cell_phone_number' not in data \
            or 'model_id' not in data \
            or 'photos' not in data:
        raise BadRequest('Must include name, cell_phone_number, model_id and photos.')
    username = data['username']
    cell_phone_number = data['cell_phone_number']
    model_id = int(data['model_id'])
    photos = data['photos']
    # User
    user = User.query.filter_by(username=username, cell_phone_number=cell_phone_number).first()
    if not user:
        u = User()
        u.from_dict(data)
        db.session.add(u)
        user = User.query.filter_by(username=username, cell_phone_number=cell_phone_number).first()
    # FaceModel
    face_model = FaceModel.query.filter_by(id=model_id).first()
    if not face_model:
        raise BadRequest('Unsupported model.')
    # Photo
    photo_count = Photo.query.filter_by(id=user.id).count()
    for i in range(len(photos)):
        input_image = file_utils.resize_blob_to(photos[i])
        embedding = facenet.get_embeddings(input_image)
        data, ext = file_utils.split_data_ext(photos[i])
        photo_name = '{}-{}-{}'.format(user.username, user.cell_phone_number, str(i + 1 + photo_count))
        photo_name = '{}.{}'.format(encryption_utils.encrypt(photo_name), ext)
        file_utils.save_photo(data, photo_name)
        p = Photo(photo_name=photo_name, embedding=pickle.dumps(embedding), user_id=user.id,
                  model_id=model_id, model_name=face_model.model_name)
        db.session.add(p)
    db.session.commit()
    return jsonify(success=True, data=None, status_code=200)


@bp.route('/user', methods=['DELETE'])
def delete_user():
    user_id = request.args.get('id', default=0, type=int)
    if not user_id:
        raise BadRequest('Illegal Request!')

    user = User.query.filter_by(id=user_id).first()
    if not user:
        raise BadRequest('Illegal argument!')

    photos = Photo.query.filter_by(user_id=user_id).all()
    if photos:
        for photo in photos:
            file_utils.remove_photo(photo.photo_name)
            db.session.delete(photo)
    db.session.delete(user)
    db.session.commit()
    return jsonify(success=True, data=None, status_code=200)
